package com.vst.orm.dao.jpa;

import java.io.Serializable;
import java.util.List;

import com.vst.orm.dao.BaseDAODispatcher;
import com.vst.orm.dao.DAODispatcherException;
import com.vst.orm.search.ExampleOptions;
import com.vst.orm.search.Filter;
import com.vst.orm.search.ISearch;
import com.vst.orm.search.SearchResult;

public class DAODispatcher
  extends BaseDAODispatcher
  implements GeneralDAO
{
  protected GeneralDAO generalDAO;
  
  public void setGeneralDAO(GeneralDAO generalDAO)
  {
    this.generalDAO = generalDAO;
  }
  
  public int count(ISearch search)
  {
    Object specificDAO = getSpecificDAO(search.getSearchClass().getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return ((GenericDAO)specificDAO).count(search);
      }
      return ((Integer)callMethod(specificDAO, "count", new Object[] { search })).intValue();
    }
    return this.generalDAO.count(search);
  }
  
  public <T> T find(Class<T> type, Serializable id)
  {
    Object specificDAO = getSpecificDAO(type.getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return (T)((GenericDAO)specificDAO).find(id);
      }
      return (T)callMethod(specificDAO, "find", new Object[] { id });
    }
    return (T)this.generalDAO.find(type, id);
  }
  
  public <T> T[] find(Class<T> type, Serializable... ids)
  {
    Object specificDAO = getSpecificDAO(type.getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return (T[])((GenericDAO)specificDAO).find(ids);
      }
      return (T[])callMethod(specificDAO, "find", (Object[])ids);
    }
    return (T[])this.generalDAO.find(type, ids);
  }
  
  public <T> List<T> findAll(Class<T> type)
  {
    Object specificDAO = getSpecificDAO(type.getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return ((GenericDAO)specificDAO).findAll();
      }
      return (List)callMethod(specificDAO, "findAll", new Object[0]);
    }
    return this.generalDAO.findAll(type);
  }
  
  /**
   * @deprecated
   */
  public void flush()
  {
    throw new DAODispatcherException("The flush() method cannot be used with DAODispatcher because it could does not include a Class type to dispatch to. Use flush(Class<?>).");
  }
  
  public void flush(Class<?> klass)
  {
    Object specificDAO = getSpecificDAO(klass.getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        ((GenericDAO)specificDAO).flush();
      } else {
        callMethod(specificDAO, "flush", new Object[0]);
      }
    }
    else {
      this.generalDAO.flush();
    }
  }
  
  public <T> T getReference(Class<T> type, Serializable id)
  {
    Object specificDAO = getSpecificDAO(type.getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return (T)((GenericDAO)specificDAO).getReference(id);
      }
      return (T)callMethod(specificDAO, "getReference", new Object[] { id });
    }
    return (T)this.generalDAO.getReference(type, id);
  }
  
  public <T> T[] getReferences(Class<T> type, Serializable... ids)
  {
    Object specificDAO = getSpecificDAO(type.getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return (T[])((GenericDAO)specificDAO).getReferences(ids);
      }
      return (T[])callMethod(specificDAO, "getReferences", (Object[])ids);
    }
    return this.generalDAO.getReferences(type, ids);
  }
  
  public boolean isAttached(Object entity)
  {
    Object specificDAO = getSpecificDAO(entity.getClass().getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return ((GenericDAO)specificDAO).isAttached(entity);
      }
      return ((Boolean)callMethod(specificDAO, "isAttached", new Object[] { entity })).booleanValue();
    }
    return this.generalDAO.isAttached(entity);
  }
  
  public void refresh(Object... entities)
  {
    Class<?> type = getUniformArrayType(entities);
    if (type == null) {
      return;
    }
    if (type.equals(Object.class))
    {
      for (Object entity : entities) {
        refresh(new Object[] { entity });
      }
      return;
    }
    Object specificDAO = getSpecificDAO(type.getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        ((GenericDAO)specificDAO).refresh(entities);
      } else {
        callMethod(specificDAO, "refresh", entities);
      }
    }
    else {
      this.generalDAO.refresh(entities);
    }
  }
  
  public boolean remove(Object entity)
  {
    Object specificDAO = getSpecificDAO(entity.getClass().getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return ((GenericDAO)specificDAO).remove(entity);
      }
      return ((Boolean)callMethod(specificDAO, "remove", new Object[] { entity })).booleanValue();
    }
    return this.generalDAO.remove(entity);
  }
  
  public void remove(Object... entities)
  {
    Class<?> type = getUniformArrayType(entities);
    if (type == null) {
      return;
    }
    if (type.equals(Object.class))
    {
      for (Object entity : entities) {
        remove(entity);
      }
      return;
    }
    Object specificDAO = getSpecificDAO(type.getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        ((GenericDAO)specificDAO).remove(entities);
      } else {
        callMethod(specificDAO, "remove", entities);
      }
    }
    else {
      this.generalDAO.remove(entities);
    }
  }
  
  public boolean removeById(Class<?> type, Serializable id)
  {
    Object specificDAO = getSpecificDAO(type.getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return ((GenericDAO)specificDAO).removeById(id);
      }
      return ((Boolean)callMethod(specificDAO, "removeById", new Object[] { id })).booleanValue();
    }
    return this.generalDAO.removeById(type, id);
  }
  
  public void removeByIds(Class<?> type, Serializable... ids)
  {
    Object specificDAO = getSpecificDAO(type.getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        ((GenericDAO)specificDAO).removeByIds(ids);
      } else {
        callMethod(specificDAO, "removeByIds", (Object[])ids);
      }
    }
    else {
      this.generalDAO.removeByIds(type, ids);
    }
  }
  
  public <T> T save(T entity)
  {
    Object specificDAO = getSpecificDAO(entity.getClass().getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return (T)((GenericDAO)specificDAO).save(entity);
      }
      return (T)callMethod(specificDAO, "save", new Object[] { entity });
    }
    return (T)this.generalDAO.save(entity);
  }
  
  public Object[] save(Object... entities)
  {
    if (entities == null) {
      return null;
    }
    Class<?> type = getUniformArrayType(entities);
    if (type == null) {
      return new Object[entities.length];
    }
    if (type.equals(Object.class))
    {
      Object[] retVal = new Object[entities.length];
      for (int i = 0; i < entities.length; i++) {
        retVal[i] = save(entities[i]);
      }
      return retVal;
    }
    Object specificDAO = getSpecificDAO(type.getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return ((GenericDAO)specificDAO).save(entities);
      }
      return (Object[])callMethod(specificDAO, "save", entities);
    }
    return this.generalDAO.save(entities);
  }
  
  public List search(ISearch search)
  {
    Object specificDAO = getSpecificDAO(search.getSearchClass().getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return ((GenericDAO)specificDAO).search(search);
      }
      return (List)callMethod(specificDAO, "search", new Object[] { search });
    }
    return this.generalDAO.search(search);
  }
  
  public SearchResult searchAndCount(ISearch search)
  {
    Object specificDAO = getSpecificDAO(search.getSearchClass().getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return ((GenericDAO)specificDAO).searchAndCount(search);
      }
      return (SearchResult)callMethod(specificDAO, "searchAndCount", new Object[] { search });
    }
    return this.generalDAO.searchAndCount(search);
  }
  
  public Object searchUnique(ISearch search)
  {
    Object specificDAO = getSpecificDAO(search.getSearchClass().getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return ((GenericDAO)specificDAO).searchUnique(search);
      }
      return callMethod(specificDAO, "searchUnique", new Object[] { search });
    }
    return this.generalDAO.searchUnique(search);
  }
  
  public Filter getFilterFromExample(Object example)
  {
    Object specificDAO = getSpecificDAO(example.getClass().getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return ((GenericDAO)specificDAO).getFilterFromExample(example);
      }
      return (Filter)callMethod(specificDAO, "getFilterFromExample", new Object[] { example });
    }
    return this.generalDAO.getFilterFromExample(example);
  }
  
  public Filter getFilterFromExample(Object example, ExampleOptions options)
  {
    Object specificDAO = getSpecificDAO(example.getClass().getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return ((GenericDAO)specificDAO).getFilterFromExample(example, options);
      }
      return (Filter)callMethod(specificDAO, "getFilterFromExample", new Object[] { example, options });
    }
    return this.generalDAO.getFilterFromExample(example, options);
  }
  
  public <T> T merge(T entity)
  {
    Object specificDAO = getSpecificDAO(entity.getClass().getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return (T)((GenericDAO)specificDAO).merge(entity);
      }
      return (T)callMethod(specificDAO, "merge", new Object[] { entity });
    }
    return (T)this.generalDAO.merge(entity);
  }
  
  public Object[] merge(Object... entities)
  {
    if (entities == null) {
      return null;
    }
    Class<?> type = getUniformArrayType(entities);
    if (type == null) {
      return new Object[entities.length];
    }
    if (type.equals(Object.class))
    {
      Object[] retVal = new Object[entities.length];
      for (int i = 0; i < entities.length; i++) {
        retVal[i] = merge(entities[i]);
      }
      return retVal;
    }
    Object specificDAO = getSpecificDAO(type.getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        return ((GenericDAO)specificDAO).merge(entities);
      }
      return (Object[])callMethod(specificDAO, "merge", entities);
    }
    return this.generalDAO.merge(entities);
  }
  
  public void persist(Object... entities)
  {
    Class<?> type = getUniformArrayType(entities);
    if (type == null) {
      return;
    }
    if (type.equals(Object.class))
    {
      for (Object entity : entities) {
        persist(new Object[] { entity });
      }
      return;
    }
    Object specificDAO = getSpecificDAO(type.getName());
    if (specificDAO != null)
    {
      if ((specificDAO instanceof GenericDAO)) {
        ((GenericDAO)specificDAO).persist(entities);
      } else {
        callMethod(specificDAO, "persist", entities);
      }
    }
    else {
      this.generalDAO.persist(entities);
    }
  }
}

